<template>
	<view>
		<!-- #ifdef APP-PLUS || H5 -->
		<view ref="flowCanvas" class="canvas py-flow-rotate" id="flowCanvas" :data="flowData"
			:change:data="appBmpn.getFlowData" :hasStart="hasStart" :change:hasStart="appBmpn.getHasStart">
		</view>
		<!-- #endif -->


		<!-- #ifdef MP-WEIXIN -->
		<view id="py-bpmn-tree-node">
			<py-bpmn-tree-node :treeData="treeData" :hasStart="hasStart"></py-bpmn-tree-node>
		</view>
		<!-- #endif -->


	</view>
</template>

<script>
	// #ifdef MP-WEIXIN
	import x2js from "@/utils/x2js/x2js/wx-x2js";
	// #endif

	export default {
		name: "PyBpmnViewer",
		props: {
			// 回显数据传值
			flowData: {
				type: Object,
				default: () => {},
				required: false
			},
			
			// 是否启动
			hasStart: {
				type: Boolean,
				default: true
			}
		},
		data() {
			return {
				treeData: {},
				// 已经加入节点的id信息
				treeDataIdList: []
			}
		},
		// #ifdef MP-WEIXIN
		mounted() {
			this.init();
		},
		methods: {

			init() {
				let currentX2js = new x2js();
				let jsonObject = currentX2js.xml2js(this.flowData.xmlData);

				// 判断流程是否启动
				let nodeData = this.flowData.nodeData;
				
				this.getBpmnList(jsonObject);
			},


			// 通过bpmn的jsonObject配置获取bpmn列表
			getBpmnList(jsonObject) {
				let process = jsonObject && jsonObject.definitions && jsonObject.definitions.process;
				if (process) {

					let startEvent = process.startEvent;
					let copyStartEvent = JSON.parse(JSON.stringify(startEvent));
					copyStartEvent["type"] = "start";
					let nodeData = this.flowData.nodeData;
					let startNode = nodeData.find(m => m.key == startEvent._id);
					copyStartEvent["completed"] = startNode ? startNode.completed : false;
					let bpmnTree = {
						...copyStartEvent
					}
					let id = startEvent._id;
					this.treeData = bpmnTree;
					this.getBpmnTree(id, process, bpmnTree);

				}
			},

			// 递归获取bpmn树形结构
			getBpmnTree(id, process, bpmnTree) {
				let sequenceFlow = process.sequenceFlow;
				if (!sequenceFlow || !sequenceFlow.length) {
					this.$modal.msgError("流程图不完整");
					return;
				}

				let nodeData = this.flowData.nodeData;

				let childrenIdList = sequenceFlow.filter(item => item._sourceRef == id).map(item => item._targetRef);

				let userTask = process.userTask;
				if (userTask) {
					if (userTask.length && userTask.length > 0) {
						let childrenUserTask = userTask.filter(item => childrenIdList.includes(item._id));
						if (childrenUserTask.length > 0) {
							if (!Object.hasOwnProperty.call(bpmnTree, "children")) {
								bpmnTree.children = [];
							}

							childrenUserTask.forEach(currentUserTask => {
								let copyCurrentUserTask = JSON.parse(JSON.stringify(currentUserTask));
								copyCurrentUserTask["type"] = "user";
								let currentNode = nodeData.find(m => m.key == currentUserTask._id);
								copyCurrentUserTask["completed"] = currentNode ? currentNode.completed : false;
								bpmnTree.children.push(copyCurrentUserTask);
								let currentId = currentUserTask._id;
								if (!this.treeDataIdList.includes(currentId)) {
									this.treeDataIdList.push(currentId);
									this.getBpmnTree(currentId, process, copyCurrentUserTask);
								} else {
									copyCurrentUserTask._id = currentId + "_remove_" + new Date().getTime();
								}

							});
						}


					} else if (Object.hasOwnProperty.call(userTask, "_id") && childrenIdList.includes(userTask._id)) {
						if (!Object.hasOwnProperty.call(bpmnTree, "children")) {
							bpmnTree.children = [];
						}
						let copyUserTask = JSON.parse(JSON.stringify(userTask));
						copyUserTask["type"] = "user";
						let currentNode = nodeData.find(m => m.key == userTask._id);
						copyUserTask["completed"] = currentNode ? currentNode.completed : false;
						bpmnTree.children.push(copyUserTask);
						let currentId = userTask._id;
						if (!this.treeDataIdList.includes(currentId)) {
							this.treeDataIdList.push(currentId);
							this.getBpmnTree(currentId, process, copyUserTask);
						} else {
							copyUserTask._id = currentId + "_remove_" + new Date().getTime();
						}

					}
				}

				let exclusiveGateway = process.exclusiveGateway;
				if (exclusiveGateway) {
					if (exclusiveGateway.length && exclusiveGateway.length > 0) {
						let childrenExclusiveGateway = exclusiveGateway.filter(item => childrenIdList.includes(item._id));
						if (childrenExclusiveGateway.length > 0) {
							if (!Object.hasOwnProperty.call(bpmnTree, "children")) {
								bpmnTree.children = [];
							}

							childrenExclusiveGateway.forEach(currentExclusiveGateway => {
								let copyCurrentExclusiveGateway = JSON.parse(JSON.stringify(
									currentExclusiveGateway));
								copyCurrentExclusiveGateway["type"] = "gateway";
								let currentNode = nodeData.find(m => m.key == currentExclusiveGateway._id);
								copyCurrentExclusiveGateway["completed"] = currentNode ? currentNode.completed :
									false;
								bpmnTree.children.push(copyCurrentExclusiveGateway);
								let currentId = currentExclusiveGateway._id;
								if (!this.treeDataIdList.includes(currentId)) {
									this.treeDataIdList.push(currentId);
									this.getBpmnTree(currentId, process, copyCurrentExclusiveGateway);
								} else {
									copyCurrentExclusiveGateway._id = currentId + "_remove_" + new Date()
										.getTime();
								}

							});
						}

					} else if (Object.hasOwnProperty.call(exclusiveGateway, "_id") && childrenIdList.includes(
							exclusiveGateway._id)) {
						if (!Object.hasOwnProperty.call(bpmnTree, "children")) {
							bpmnTree.children = [];
						}
						let copyExclusiveGateway = JSON.parse(JSON.stringify(exclusiveGateway));
						copyExclusiveGateway["type"] = "gateway";
						let currentNode = nodeData.find(m => m.key == exclusiveGateway._id);
						copyExclusiveGateway["completed"] = currentNode ? currentNode.completed : false;
						bpmnTree.children.push(copyExclusiveGateway);
						let currentId = exclusiveGateway._id;
						if (!this.treeDataIdList.includes(currentId)) {
							this.treeDataIdList.push(currentId);
							this.getBpmnTree(currentId, process, copyExclusiveGateway);
						} else {
							copyExclusiveGateway._id = currentId + "_remove_" + new Date().getTime();
						}

					}
				}

				let endEvent = process.endEvent;
				if (endEvent) {
					if (endEvent.length && endEvent.length > 0) {
						let childrenEndEvent = endEvent.filter(item => childrenIdList.includes(item._id));
						if (childrenEndEvent.length > 0) {
							if (!Object.hasOwnProperty.call(bpmnTree, "children")) {
								bpmnTree.children = [];
							}

							childrenEndEvent.forEach(currentEndEvent => {
								let copyCurrentEndEvent = JSON.parse(JSON.stringify(currentEndEvent));
								copyCurrentEndEvent["type"] = "end";
								let currentNode = nodeData.find(m => m.key == currentEndEvent._id);
								copyCurrentEndEvent["completed"] = currentNode ? currentNode.completed : false;
								bpmnTree.children.push(copyCurrentEndEvent);
								let currentId = currentEndEvent._id;
								if (!this.treeDataIdList.includes(currentId)) {
									this.treeDataIdList.push(currentId);
									this.getBpmnTree(currentId, process, copyCurrentEndEvent);
								} else {
									copyCurrentEndEvent._id = currentId + "_remove_" + new Date().getTime();
								}

							});
						}

					} else if (Object.hasOwnProperty.call(endEvent, "_id") && childrenIdList.includes(endEvent._id)) {
						if (!Object.hasOwnProperty.call(bpmnTree, "children")) {
							bpmnTree.children = [];
						}
						let copyEndEvent = JSON.parse(JSON.stringify(endEvent));
						copyEndEvent["type"] = "end";
						let currentNode = nodeData.find(m => m.key == endEvent._id);
						copyEndEvent["completed"] = currentNode ? currentNode.completed : false;
						bpmnTree.children.push(copyEndEvent);
						let currentId = endEvent._id;
						if (!this.treeDataIdList.includes(currentId)) {
							this.treeDataIdList.push(currentId);
							this.getBpmnTree(currentId, process, copyEndEvent);
						} else {
							copyEndEvent._id = currentId + "_remove_" + new Date().getTime();
						}

					}
				}
			},

			// 判断是否流程节点已经保存
			hasSaveFlowNode(treeData, id) {
				let currentId = treeData && treeData._id;
				if (currentId == id) {
					return true;
				}

				if (Object.hasOwnProperty.call(treeData, "children")) {
					if (this.hasSaveFlowNode(treeData["children"], id)) {
						return true;
					}
				}
				return false;
			}


		},
		// #endif
	}
</script>

<!-- #ifdef APP-PLUS || H5-->
<script module="appBmpn" lang="renderjs">
	import BpmnViewer from "bpmn-js";
	export default {
		data() {
			return {
				renderFlowData: {},
				renderHasStart: true,
				renderBpmnViewer: undefined
			}
		},
		mounted() {
			this.initBpmn();
		},
		methods: {
			getFlowData(data) {
				this.renderFlowData = data;
			},
			getHasStart(hasStart) {
				this.renderHasStart = hasStart;
			},
			initBpmn() {
				// 获取到属性ref为“canvas”的dom节点
				// 生成实例
				this.renderBpmnViewer && this.renderBpmnViewer.destroy();
				let flowCanvas = document.getElementById("flowCanvas");

				this.renderBpmnViewer = new BpmnViewer({
					container: flowCanvas,
					width: "100%"
				});
				this.loadFlowCanvas(this.renderFlowData);
			},

			// 加载流程图片
			async loadFlowCanvas(flowData) {
				try {
					await this.renderBpmnViewer.importXML(flowData.xmlData);
					await this.fitViewport();

					// 流程线高亮设置
					if (flowData.nodeData && flowData.nodeData.length > 0 && this.renderHasStart) {
						await this.fillColor(flowData.nodeData);
					}
				} catch (err) {
					console.error(err.message, err.warnings)
				}
			},

			// 让图能自适应屏幕
			fitViewport() {
				this.renderBpmnViewer.get('canvas').zoom("fit-viewport", "auto");
			},

			// 设置高亮颜色的
			fillColor(nodeData) {
				const canvas = this.renderBpmnViewer.get('canvas')
				this.renderBpmnViewer.getDefinitions().rootElements[0].flowElements.forEach(n => {
					const completeTask = nodeData.find(m => m.key === n.id)
					const todoTask = nodeData.find(m => !m.completed)
					const endTask = nodeData[nodeData.length - 1]
					if (n.$type === 'bpmn:UserTask') {
						if (completeTask) {
							canvas.addMarker(n.id, completeTask.completed ? 'highlight' : 'highlight-todo')
							n.outgoing?.forEach(nn => {
								const targetTask = nodeData.find(m => m.key === nn.targetRef.id)
								if (targetTask) {
									if (todoTask && completeTask.key === todoTask.key && !todoTask
										.completed) {
										canvas.addMarker(nn.id, todoTask.completed ? 'highlight' :
											'highlight-todo')
										canvas.addMarker(nn.targetRef.id, todoTask.completed ?
											'highlight' : 'highlight-todo')
									} else {
										canvas.addMarker(nn.id, targetTask.completed ? 'highlight' :
											'highlight-todo')
										canvas.addMarker(nn.targetRef.id, targetTask.completed ?
											'highlight' : 'highlight-todo')
									}
								}
							})
						}
					}
					// 排他网关
					else if (n.$type === 'bpmn:ExclusiveGateway') {
						if (completeTask) {
							canvas.addMarker(n.id, completeTask.completed ? 'highlight' : 'highlight-todo')
							n.outgoing?.forEach(nn => {
								const targetTask = nodeData.find(m => m.key === nn.targetRef.id)
								if (targetTask) {

									canvas.addMarker(nn.id, targetTask.completed ? 'highlight' :
										'highlight-todo')
									canvas.addMarker(nn.targetRef.id, targetTask.completed ? 'highlight' :
										'highlight-todo')
								}
							})
						}
					}
					// 并行网关
					else if (n.$type === 'bpmn:ParallelGateway') {
						if (completeTask) {
							canvas.addMarker(n.id, completeTask.completed ? 'highlight' : 'highlight-todo')
							n.outgoing?.forEach(nn => {
								const targetTask = nodeData.find(m => m.key === nn.targetRef.id)
								if (targetTask) {
									canvas.addMarker(nn.id, targetTask.completed ? 'highlight' :
										'highlight-todo')
									canvas.addMarker(nn.targetRef.id, targetTask.completed ? 'highlight' :
										'highlight-todo')
								}
							})
						}
					} else if (n.$type === 'bpmn:StartEvent') {
						n.outgoing.forEach(nn => {
							const completeTask = nodeData.find(m => m.key === nn.targetRef.id)
							if (completeTask) {
								canvas.addMarker(nn.id, 'highlight')
								canvas.addMarker(n.id, 'highlight')
								return;
							}
						})
					} else if (n.$type === 'bpmn:EndEvent') {
						if (endTask.key === n.id && endTask.completed) {
							canvas.addMarker(n.id, 'highlight')
							return;
						}
					}
				})
			}


		},
	}
</script>
<!-- #endif -->
<style lang="css" scoped>
	/* 隐藏下方流程名称和bpmn图标 */
	/deep/ .bjs-breadcrumbs,
	/deep/ .bjs-powered-by {
		display: none
	}

	.py-flow-rotate {
		background-color: white;
		margin: 10rpx 10rpx 10rpx 10rpx;
		height: calc(100vw - 20rpx);
		width: 100vh;
		-webkit-transform: rotate(90deg);
		-webkit-transform-origin: calc(50vw - 10rpx) calc(50vw - 10rpx);
		transform: rotate(90deg);
	}

	/* #ifdef APP-PLUS || H5 */
	/* 设置bmpn-js高亮样式 */
	/deep/ .highlight.djs-shape .djs-visual> :nth-child(1) {
		fill: #56bb56 !important;
		stroke: #56bb56 !important;
		fill-opacity: 0.2 !important;
	}

	/deep/ .highlight.djs-shape .djs-visual> :nth-child(2) {
		fill: #56bb56 !important;
	}

	/deep/ .highlight.djs-shape .djs-visual>path {
		fill: #56bb56 !important;
		fill-opacity: 0.2 !important;
		stroke: #56bb56 !important;
	}

	/deep/ .highlight.djs-connection>.djs-visual>path {
		stroke: #56bb56 !important;
	}

	/deep/ .highlight-todo.djs-connection>.djs-visual>path {
		stroke: #eab24a !important;
		stroke-dasharray: 4px !important;
		fill-opacity: 0.2 !important;
	}

	/deep/ .highlight-todo.djs-shape .djs-visual> :nth-child(1) {
		stroke-dasharray: 5, 5;
		stroke-dashoffset: 500;
		stroke: #eab24a !important;
		fill: rgba(252, 211, 127, 0.2) !important;
	}

	/* #endif */
</style>